﻿//-----------------------------------------------------------------------
// <copyright file="MessengerEntity.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Microsoft.Live.Messenger
{
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Windows.Browser;

    internal sealed class MessengerEntity
    {
        private readonly ScriptObject scriptObject;

        public MessengerEntity(object obj)
        {
            Debug.Assert(obj != null);
            Debug.Assert(obj is ScriptObject);

            ScriptObject scriptObject = obj as ScriptObject;
            if (scriptObject == null)
            {
                throw new ArgumentException();
            }
            this.scriptObject = scriptObject;
        }

        public static MessengerEntity CreateInstance(Type type, params object[] args)
        {
            string name = type.FullName;

            ConvertParams(args);

            return new MessengerEntity(HtmlPage.Window.CreateInstance(name, args));
        }

        private static string Normalize(string s)
        {
            if (String.IsNullOrEmpty(s))
            {
                return string.Empty;
            }
            if (s.Length == 1)
            {
                return s.ToLower();
            }
            return s.Substring(0, 1).ToLower() + s.Substring(1);
        }

        public object GetItem(int index)
        {
            return GetItem<object>(index);
        }

        public T GetItem<T>(int index)
        {
            return (T)this.scriptObject.Invoke("get_Item", index);
        }

        public object GetItem(string name)
        {
            return GetItem<object>(name);
        }

        public T GetItem<T>(string name)
        {
            return (T)this.scriptObject.Invoke("get_Item", name);
        }

        public object GetProperty(string name)
        {
            return GetProperty<object>(name);
        }

        public T GetProperty<T>(string name)
        {
            name = "get_" + Normalize(name);

            return (T)this.scriptObject.Invoke(name);
        }

        public T GetNumberProperty<T>(string name)
        {
            double d = GetProperty<double>(name);

            return (T)(object)Convert.ToInt32(d);
        }

        public T GetEnumProperty<T>(string name)
        {
            return GetNumberProperty<T>(name);
        }

        public Uri GetUriProperty(string name)
        {
            object uri = GetProperty(name);
            if (uri == null)
            {
                return null;
            }

            MessengerEntity uriEntity = new MessengerEntity(uri);

            return new Uri((string)uriEntity.Invoke("ToString"));
        }

        public Guid GetGuidProperty(string name)
        {
            object guid = GetProperty(name);
            if (guid == null)
            {
                return Guid.Empty;
            }

            MessengerEntity guidEntity = new MessengerEntity(guid);

            return new Guid((string)guidEntity.Invoke("ToString"));
        }

        public Version GetVersionProperty(string name)
        {
            object version = GetProperty(name);
            if (version == null)
            {
                return new Version(0, 0);
            }

            MessengerEntity versionEntity = new MessengerEntity(version);

            return new Version(
                versionEntity.GetNumberProperty<int>("Major"),
                versionEntity.GetNumberProperty<int>("Minor"),
                versionEntity.GetNumberProperty<int>("Build"),
                versionEntity.GetNumberProperty<int>("Revision"));
        }

        public void SetProperty<T>(string name, T value)
        {
            name = "set_" + Normalize(name);

            this.scriptObject.Invoke(name, value);
        }

        public object Invoke(string name, params object[] args)
        {
            name = Normalize(name);

            ConvertParams(args);

            return this.scriptObject.Invoke(name, args);
        }

        private static void ConvertParams(object[] args)
        {
            for (int i = 0; i < args.Length; i++)
            {
                MessengerEntity entityArg = args[i] as MessengerEntity;
                if (entityArg != null)
                {
                    args[i] = entityArg.scriptObject;
                }
            }
        }

        public void AttachEvent(string name, MessengerEventHandler handler)
        {
            Debug.Assert(!String.IsNullOrEmpty(name));

            name = "add_" + Normalize(name);

            ScriptEventHandler scriptHandler = delegate(ScriptObject sender, ScriptObject e)
            {
                if (handler != null)
                {
                    handler(this, new MessengerEventArgs(new MessengerEntity(e)));
                }
            };

            this.scriptObject.Invoke(name, CreateDelegate(scriptHandler));
        }

        public static object Invoke(Type type, string name, params object[] args)
        {
            string[] parts = type.FullName.Split('.');

            ScriptObject current = HtmlPage.Window;

            for (int i = 0; i < parts.Length; i++)
            {
                current = (ScriptObject)current.GetProperty(parts[i]);
            }

            name = Normalize(name);

            ConvertParams(args);

            return current.Invoke(name, args);
        }

        public static object GetProperty(Type type, string name)
        {
            return GetProperty<object>(type, name);
        }

        public static T GetProperty<T>(Type type, string name)
        {
            string[] parts = type.FullName.Split('.');

            ScriptObject current = HtmlPage.Window;

            for (int i = 0; i < parts.Length; i++)
            {
                current = (ScriptObject)current.GetProperty(parts[i]);
            }

            name = "get_" + Normalize(name);

            return (T)current.Invoke(name);
        }


        private ScriptObject CreateDelegate(ScriptEventHandler handler)
        {
            ScriptObject function = (ScriptObject)HtmlPage.Window.GetProperty("Function");

            return (ScriptObject)function.Invoke("fromSilverlight", handler);
        }

        public override int GetHashCode()
        {
            return scriptObject.GetHashCode();
        }

        public override bool Equals(object other)
        {
            return Equals(other as MessengerEntity);
        }

        public bool Equals(MessengerEntity other)
        {
            if (other == null)
            {
                return false;
            }

            return Object.ReferenceEquals(this.scriptObject, other.scriptObject);
        }
    }
}